package com.ebs.md.fxmonitor.marketmonitor.config;

/**
 * Perforce Revision:
 *  $File: //depot/MarketData/fxmonitor/main/src/main/java/com/ebs/md/fxmonitor/marketmonitor/config/EventConfigProviderCcc.java $
 *  $Revision: #26 $  $Change: 115845 $  $DateTime: 2012/10/17 08:14:53 $  $Author: Aviad.Yosef $
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.util.Assert;

import com.ebs.commons.logger.MessageText;
import com.ebs.md.fxmonitor.marketmonitor.MarketMonitorConsts;
import com.ebsdr.fsp.ccc.cccconfig.CccConfig;
import com.ebsdr.fsp.ccc.client.ClientAppDescriptor;
import com.ebsdr.fsp.ccc.client.ConfigurationClientException;
import com.ebsdr.fsp.ccc.client.ConfigurationClientFactory;
import com.ebsdr.fsp.ccc.client.IAppConfigurationEventListener;
import com.ebsdr.fsp.ccc.client.IConfigurationClient;
import com.ebsdr.fsp.ccc.client.IllegalOperationException;
import com.ebsdr.fsp.ccc.client.configsource.IConfigurationSourceResources;
import com.ebsdr.fsp.ccc.client.events.IAppConfigurationEvent;
import com.ebsdr.fsp.ccc.configuration.IConfiguration;
import com.ebsdr.fsp.ccc.configuration.ConfigurationViewer.ConfigViewerToken;


public class EventConfigProviderCcc implements IEventConfigProvider, MarketMonitorConsts{
	
    final protected Logger logger = Logger.getLogger(getClass());

    final static String CCCnameOfConfig = "MonitoredMarketActivities";
    
	protected IConfigurationClient configClient;
	
	private String appName     = "FSP";
	private String appVersion  = "1.0";
	private String appInstance = "1";

	// attempt fails if in the middle of reading configuration from CCC the configuration changes. CCC then fails the attempt
	private int maxConfigReadAttampts = 3;
	
	private IConfigurationSourceResources externalConfigResources;
	
	private IAppConfigurationEventListener mainCccListener;
	
	private List<IAppConfigurationEventListener> extraCccListeners;
	
	private CccConfig cccConfig;

	private long         crossMarketIntervalThreshold;
	private long         offMarketAmountThreshold;
	private Map<String, Integer> offMarketCurrencyPairPips;
	private long         centralBankAmountThreshold;
	private List<String> centralBankActivityFloorNameList;
	private List<String> topOfBookChangeCurrencyPairBlackListed;

	//alert configuration listener 
	private List<IConfigListener> configListeners;
	
	
	public EventConfigProviderCcc()
	{
		crossMarketIntervalThreshold     = EVENT_CROSS_MARKET_DEFAULT_INTERVAL_THRESHOLD;
		offMarketAmountThreshold         = EVENT_OFF_MARKET_DEFAULT_QUOTE_AMOUNT_THRESHOLD;
		offMarketCurrencyPairPips        = new HashMap<String, Integer>(); 
		centralBankAmountThreshold       = EVENT_CENTRAL_BANK_DEFAULT_QUOTE_AMOUNT_THRESHOLD;
		centralBankActivityFloorNameList = new ArrayList<String>();
		topOfBookChangeCurrencyPairBlackListed  = new ArrayList<String>();
	}
	
	public void init(){
		Assert.notEmpty(configListeners, "configListeners should not be empty");
		boolean exceptionThrown = false;
		try {
			ClientAppDescriptor appDescriptor = new ClientAppDescriptor(this.appName, this.appVersion, this.appInstance);
			ConfigurationClientFactory cccFactory = new ConfigurationClientFactory();
			this.configClient = cccFactory.getConfigurationClient(appDescriptor, externalConfigResources, this.cccConfig);
			this.mainCccListener = new ConfigurationChangedListener();
			this.configClient.start(this.mainCccListener);

		}
		catch (IllegalOperationException e) {
			exceptionThrown = true;
			logger.error(new MessageText("FXMON_0700101"), e);
		}
		catch (ConfigurationClientException e) {
			exceptionThrown = true;
			logger.error(new MessageText("FXMON_0700102"), e);
		}
		catch (Exception e) {
			exceptionThrown = true;
			logger.error(new MessageText("FXMON_0700103", e.getMessage()), e);
		}

		if ( !exceptionThrown ) {
			try {
				readEventConfigProviderCfg();
			} catch (NoCCCConfigException e) {
				exceptionThrown = true;
				logger.error(new MessageText("FXMON_0700106", e.getMessage()));
			}
		}
		
		if ( !exceptionThrown ) {
			notifyExtraCccListeners(null);
		} else {
			logger.warn(new MessageText("FXMON_0700111"));
			logger.warn(new MessageText("FXMON_0700109"));
		}
	}
	
	public void readEventConfigProviderCfg()
		throws NoCCCConfigException
	{
		try {
			retrieveEventConfigProviderCfgFromCCC();
		} catch (IllegalOperationException e) {
			logger.error(new MessageText("FXMON_0700104"), e);
		} catch (ConfigurationClientException e) {
			logger.error(new MessageText("FXMON_0700106"), e);
		} catch (NoCCCConfigException e) {
			// forward it
			throw e;
		}
	}
	
	private void retrieveEventConfigProviderCfgFromCCC()
	throws ConfigurationClientException, NoCCCConfigException
	{
        boolean successfulRead = false;
        int readAttempts = 0;
        IConfiguration configuration = this.configClient.getConfiguration(CCCnameOfConfig);
		
        if (configuration == null) {
			throw new NoCCCConfigException(CCCnameOfConfig);
        } else {
	        while (!successfulRead && readAttempts < this.maxConfigReadAttampts) {
    			logger.info("Got configuration from the ccc");
    			logger.info("delete previous configuration info");
    			ConfigViewerToken token =  configuration.startRead();

    			readConfigOfPI_1_0(configuration);
    			printConfigOfPI_1_0();
    			
    			//here we set the new configuration input for each alert configuration. 
    			for (IConfigListener curr: configListeners) {
    				if (curr != null){
    					curr.onConfig(configuration);
    				}
    			}
    			
    			successfulRead = configuration.endRead(token);
	        }
	        
	        if (!successfulRead) {
                String errMsg = "Failed to safely read configuration in " + (readAttempts + 1) + " attempts.";
                throw new ConfigurationClientException(errMsg);
	        } else {
	            logger.info("Successfully read EventConfigProvider configuration.");
	        }
        }
		//ShutdownHandler.getInstance().registerShutdownListener(this);
	}
	
	protected void readConfigOfPI_1_0(IConfiguration configuration) {
		crossMarketIntervalThreshold = configuration.getLong("/MonitoredMarketActivity/CrossMarket/@crossMarketIntervalThreshold", EVENT_CROSS_MARKET_DEFAULT_INTERVAL_THRESHOLD);
		offMarketAmountThreshold     = configuration.getLong("/MonitoredMarketActivity/SweepAttempt/@amountThreshold", EVENT_OFF_MARKET_DEFAULT_QUOTE_AMOUNT_THRESHOLD);

		loadOffMarketCurrencyPairPips(configuration);
		
		centralBankAmountThreshold  = configuration.getLong("/MonitoredMarketActivity/CentralBankActivity/@centralBankAmountThreshold", EVENT_CENTRAL_BANK_DEFAULT_QUOTE_AMOUNT_THRESHOLD);

		centralBankActivityFloorNameList = configuration.getList("/MonitoredMarketActivity/CentralBankActivity/Floor/@name");

		topOfBookChangeCurrencyPairBlackListed = configuration.getList("/MonitoredMarketActivity/TopOfBookPersistencyPairsBlackList/CurrencyPair/@name");
	}
	
	protected void printConfigOfPI_1_0() {
		logger.info("CrossMarketThresholdInterval: " + crossMarketIntervalThreshold);
		logger.info("SweepAttemptAmountThreshold: " + offMarketAmountThreshold);

		logger.info("SweepAttemptCurrencyPairPips:" + offMarketCurrencyPairPips); 

		logger.info("CentralBankAmountThreshold:" + centralBankAmountThreshold);
		logger.info("CentralBankActivityFloorNameList:" + centralBankActivityFloorNameList);
		logger.info("TopOfBookChangeCurrencyPairBlackListed:" + topOfBookChangeCurrencyPairBlackListed);
	}
	
	private void loadOffMarketCurrencyPairPips(IConfiguration configuration) {
		List<String> ccyPairList = configuration.getList("/MonitoredMarketActivity/SweepAttempt/CurrencyPair/@name");
		int numOfCurrencyPair = ccyPairList.size();
		
		offMarketCurrencyPairPips.clear();
		
		for (int index = 1; index <= numOfCurrencyPair; index++)
		{
			String ccyPairName               = configuration.getString("/MonitoredMarketActivity/SweepAttempt/CurrencyPair[" + index + "]/@name");
			int    ccyPairPipsDeltaThreshold = configuration.getInt   ("/MonitoredMarketActivity/SweepAttempt/CurrencyPair[" + index + "]/@pipsDeltaThreshold", EVENT_OFF_MARKET_DEFAULT_PIPS_THRESHOLD);
			
			offMarketCurrencyPairPips.put(ccyPairName, ccyPairPipsDeltaThreshold);
		}
	}

	/**
	 * Release resources that may be used by the Configuration Provider.
	 */
	public void shutdown() {
		if (this.configClient != null) {
			try {
				logger.info("Stopping Central Configuration Provider...");
				this.configClient.stop();
				logger.info("Central Configuration Provider stopped successfully.");
			} catch (Exception e) {
				try {
					logger.info("Failed to shutdown Central Configuration Provider.", e);		
				}catch (Exception e1){}
			}
		}
	}
	
	
	private class ConfigurationChangedListener implements IAppConfigurationEventListener {
		
		@Override
		public void handleConfigurationEvent(IAppConfigurationEvent configEvent) {
			boolean exceptionThrown = false;
			try {
				readEventConfigProviderCfg();
			} catch (Exception e) {
				exceptionThrown = true;
				logger.error(new MessageText("FXMON_0700107", e.getMessage()), e);
			}
			
			if ( !exceptionThrown ) {
				notifyExtraCccListeners(configEvent);
			}
		}

	}
	
	private static class NoCCCConfigException
	extends Exception {

		public NoCCCConfigException(String nameOfConfig) {
			super(String.format("could not read ccc named '%s'", nameOfConfig));
		}
		
	}
	
	protected void notifyExtraCccListeners(IAppConfigurationEvent configEvent) {
		if ( extraCccListeners != null ) {
			for ( IAppConfigurationEventListener curr : extraCccListeners ) {
				curr.handleConfigurationEvent(configEvent);
			}
		}
	}
	
	private String getKeyFromBaseAndLocalIso(String baseCurrencyIso, String localCurrencyIso) 
	{
		return String.format("%s/%s", baseCurrencyIso, localCurrencyIso);
	}

	@Override
	public int getOffMarketPipsThreshold(String baseCurrencyIso, String localCurrencyIso) {
		String baesAndLocalkey = getKeyFromBaseAndLocalIso(baseCurrencyIso, localCurrencyIso); 
		Integer res = offMarketCurrencyPairPips.get(baesAndLocalkey);

		if (res == null)
		{
			res = EVENT_OFF_MARKET_DEFAULT_PIPS_THRESHOLD;
		}
		
		return res; 
	}


	@Override
	public long getOffMarketAmountThreshold(String baseCurrencyIso, String localCurrencyIso) {
		return offMarketAmountThreshold;
	}


	@Override
	public long getCrossMarketIntervalThreshold(String baseCurrencyIso, String localCurrencyIso) {
		return crossMarketIntervalThreshold;
	}


	@Override
	public boolean isCentralBankActivityMonitored(String dealCode) {
		return centralBankActivityFloorNameList.contains(dealCode);
	}


	@Override
	public Long getCentralBankActivityAmountThreshold(String dealCode) {
		return centralBankAmountThreshold;
	}

	@Override
	public boolean isTopOfBookPersistencePairsBlackListed(String baseCurrencyIso, String localCurrencyIso) {
		String baesAndLocalkey = getKeyFromBaseAndLocalIso(baseCurrencyIso, localCurrencyIso); 
		
		return topOfBookChangeCurrencyPairBlackListed.contains(baesAndLocalkey);
	}	

	public void setAppName(String appName) {
		this.appName = appName;
	}

	public void setAppVersion(String appVersion) {
		this.appVersion = appVersion;
	}

	public void setAppInstance(String appInstance) {
		this.appInstance = appInstance;
	}

	public void setExternalConfigResources(IConfigurationSourceResources externalConfigResources) {
		this.externalConfigResources = externalConfigResources;
	}

	public void setExtraCccListeners(List<IAppConfigurationEventListener> extraCccListeners) {
		this.extraCccListeners = extraCccListeners;
	}
	
	public void setCccConfig(CccConfig cccConfig) {
		this.cccConfig = cccConfig;
	}

	public void setConfigListeners(List<IConfigListener> configListeners) {
		this.configListeners = configListeners;
	}

}
